/**
 * 封装一个基于ts的WebSocket客户端类的实现
 * > 该类提供了一些方法和事件处理器，用于连接Websocket服务器、发送信息、处理接收到的信息和处理WebSocket相关的事件
 */

import {
  HeartbeatReqType,
  EVENT_TYPES,
  HeartbeatResType,
  Initiative_CloseCode
} from "./wsConstants"

// 将每个键映射到 EVENT_TYPES 中相应的值，最终得到一个字符串字面量的联合类型
type TEventType = (typeof EVENT_TYPES)[keyof typeof EVENT_TYPES]

// type TEventType = "open" | "close" | "error" | "message"
type TEvent = (data: any) => void
/* 
    Record<T, U> 是 TypeScript 提供的一个映射类型，
    它将类型 T 中的每个键都映射到类型 U。
    使用 Record<TEventType, any[]> 将 TEventType 中的每个键映射为 TEvent[] 类型，
    也就是每个键对应一个空数组
*/
interface IEventHandlers extends Record<TEventType, TEvent[]> {}

export default class SYWebSocketClient {
  url: string // 保存WebSocket服务器的URL
  websocket: WebSocket | null // WebSocket连接对象
  reconnectAttempts: number // 重连尝试次数
  maxReconnectAttempts: number // 最大重连次数
  // messageHandlers: IMessageHandlers
  eventHandlers: IEventHandlers // 事件处理器
  timeoutIds: any // 超时定时器ID
  heartbeatIntervalId: number //心跳检测定时器ID
  lastHeartbeatTime: number // 记录最后收到心跳的时间
  maxHeartbeatInterval: number //最大心跳间隔时间

  /**
   * 创建一个WebSocket客户端对象。
   *
   * @param {string} url - WebSocket服务器的URL。
   */
  constructor(url: string) {
    this.url = url
    this.websocket = null
    this.reconnectAttempts = 0
    this.maxReconnectAttempts = 10
    this.eventHandlers = {
      [EVENT_TYPES.OPEN]: [],
      [EVENT_TYPES.CLOSE]: [],
      [EVENT_TYPES.ERROR]: [],
      [EVENT_TYPES.MESSAGE]: [],
      [EVENT_TYPES.CONNECTION_STATUS_CHANGE]: []
    }
    this.timeoutIds = {}
    this.heartbeatIntervalId = 0
    this.lastHeartbeatTime = 0
    this.maxHeartbeatInterval = 10000
  }
  /**
   * 连接WebSocket服务器。
   */
  connect(type?: TEventType, data?) {
    return new Promise<void>((resolve, reject) => {
      this.websocket = new WebSocket(this.url) // 创建WebSocket连接
      this.websocket.binaryType = "arraybuffer" // 设置二进制数据类型为ArrayBuffer

      /* 监听打开 */
      this.websocket.onopen = () => {
        console.log("Websocket connection established. (WebSocket连接建立成功)") // WebSocket连接建立成功
        this.reconnectAttempts = 0 // 重连尝试次数重置
        this.dispatchEvent("open", data) // 分发监听的打开事件
        // 在连接建立后启动心跳检测
        // this.sendHeartbeat(5000)
        // 连接成功，返回resolve结果
        resolve()
      }

      /* 监听接收到数据 */
      this.websocket.onmessage = (event: MessageEvent) => {
        console.log("event", event)

        // 不是二进制数据则解析收到的消息
        const message = event.data instanceof ArrayBuffer ? event.data : JSON.parse(event.data)
        if (message.id && this.timeoutIds[message.id]) {
          // 收到响应消息时清除超时定时器
          clearTimeout(this.timeoutIds[message.id])
          delete this.timeoutIds[message.id]
        }
        if (message.type === HeartbeatResType) {
          // 处理心跳响应
          console.log("Received heartbeat response from server.")
          this.handleHeartbeat(type, data)
        } else {
          console.log(this.eventHandlers[event.type])
          if (!this.eventHandlers[event.type].length) {
            console.log(`暂时没有要处理的${this.eventHandlers[event.type]}回调`)
          }

          this.dispatchEvent("message", JSON.parse(event.data)) // 分发消息事件
        }
      }

      /* 监听出错 */
      this.websocket.onerror = (error: Event) => {
        console.error("Websocket error:", error) // WebSocket错误处理
        this.dispatchEvent("error", error) // 分发错误事件
        // 连接错误，返回reject错误
        reject({
          type: "Error",
          data: error
        })
        // 关闭 WebSocket 心跳定时器
        clearInterval(this.heartbeatIntervalId)
      }

      /* 监听关闭 */
      this.websocket.onclose = (event: CloseEvent) => {
        console.log(event)
        if (event.code === Initiative_CloseCode) {
          // 主动关闭WebSocket连接
          console.log("Frontend closed the WebSocket connection.")
          reject({
            type: "Close",
            data: `主动关闭连接`
          })
        } else {
          // 被迫关闭WebSocket连接
          console.log("Backend closed the WebSocket connection.")
          /* 断线重连 */
          this.reconnects(type, data, 2000)
        }
        // 关闭 WebSocket 心跳定时器
        clearInterval(this.heartbeatIntervalId)
      }
    })
  }

  /**
   * 发送一个WebSocket消息。
   *
   * @param {object|ArrayBuffer} message - 要发送的消息，可以是JavaScript对象或ArrayBuffer。
   * @param {number} timeout - 超时时间（毫秒），如果在指定时间内没有收到响应，则调用超时处理函数。
   * @param {function} timeoutHandler - 超时处理函数，接收一个参数：要发送的消息对象。
   */
  send(message, timeout?: number, timeoutHandler?: TEvent) {
    // 发送UTF-8编码的文本信息
    // socket.send("Hello Echo Server!");
    // 发送UTF-8编码的JSON数据
    // socket.send(JSON.stringify({ msg: "我是阿宝哥"}));
    // 发送二进制ArrayBuffer
    // const buffer = newArrayBuffer(128);
    // socket.send(buffer);
    // 发送二进制ArrayBufferView
    // const intview = new Uint32Array(buffer);
    // socket.send(intview);
    // 发送二进制Blob
    // const blob = new Blob([buffer]);
    // socket.send(blob);
    if (this.websocket && this.websocket.readyState === WebSocket.OPEN) {
      if (message instanceof ArrayBuffer) {
        this.websocket.send(message) // 发送二进制消息
      } else {
        const messageId = Math.random().toString(36).substr(2, 8) // 生成随机消息ID

        const data = JSON.stringify(message) // 将消息对象转换为JSON字符串
        this.websocket.send(data) // 发送消息

        // 超时处理
        // this.timeoutIds[messageId] = setTimeout(() => {
        //   console.warn(`WebSocket request ${messageId} timed out.`) // 超时警告
        //   delete this.timeoutIds[messageId] // 清除超时定时器ID
        //   timeoutHandler(message) // 调用超时处理函数
        // }, timeout)
      }
    } else {
      console.error("Websocket connection not open, message not sent:", message) // WebSocket连接未打开时无法发送消息
    }
  }

  /**
   * 关闭一个websocket连接
   *
   * @param code 状态码
   * @param reason
   */
  close(code?: number, reason?: string) {
    this.websocket?.close(code, reason)
    this.timeoutIds = {}
    this.heartbeatIntervalId = 0
    this.lastHeartbeatTime = 0
    this.maxHeartbeatInterval = 10000
  }

  /**
   * 添加一个WebSocket事件处理器。
   *
   * @param {string} type - 事件类型。
   * @param {function} handler - 处理函数，接收一个参数：事件数据。
   */
  addEventHandler(type: TEventType, handler: TEvent) {
    this.eventHandlers[type].push(handler) // 添加事件处理函数
  }

  /**
   * 移除一个WebSocket事件处理器。
   *
   * @param {string} type - 事件类型。
   * @param {function} handler - 处理函数。
   */
  removeEventHandler(type: TEventType, handler: TEvent) {
    const index = this.eventHandlers[type].indexOf(handler) // 查找处理函数在数组中的索引
    if (index !== -1) {
      this.eventHandlers[type].splice(index, 1) // 移除处理函数
    }
    // console.log("删除事件处理回调", this.eventHandlers[type])
  }

  /**
   * 分发 WebSocket 事件。
   *
   * @param {string} type - 事件类型。
   * @param {any} data - 事件数据。
   */
  dispatchEvent(type: TEventType, data) {
    if (this.eventHandlers[type]) {
      for (const handler of this.eventHandlers[type]) {
        handler(data) // 调用事件处理函数
      }
    }
  }

  /**
   * 断线重连
   * @param type - 事件类型
   * @param data - 事件数据
   * @param timeout -重连时间
   */
  reconnects(type: TEventType, data, timeout: number) {
    if (this.reconnectAttempts < this.maxReconnectAttempts) {
      // 判断当前 WebSocket 的状态
      if (this.websocket?.readyState !== WebSocket.OPEN) {
        // 两秒后重新连接
        setTimeout(() => {
          this.connect(type, data)
          // 重连尝试次数加1
          this.reconnectAttempts++
        }, timeout)
      }
    } else {
      console.error(`Websocket connection failed after ${this.maxReconnectAttempts} attempts.`) // 达到最大重连次数后仍未成功连接
      this.dispatchEvent("close", data) // 分发关闭事件
      // 连接错误，返回reject错误
      Promise.reject({
        type: "Close",
        data: `${new Error("Websocket connection failed")}`
      })
    }
  }

  /**
   * 发送心跳消息。
   * @param heartbeatInterval 心跳信息的延迟时间
   */
  sendHeartbeat(heartbeatInterval: number) {
    const heartbeatMessage = { type: HeartbeatReqType } // 心跳消息内容

    const sendHeartbeat = () => {
      if (this.websocket?.readyState === WebSocket.OPEN) {
        this.send(heartbeatMessage, 2000, (e) => {
          console.log(e)
        })
      }
    }

    // this.heartbeatIntervalId = setInterval(() => {
    //   sendHeartbeat()
    // }, heartbeatInterval)
  }

  /**
   * 处理接收响应信息
   */
  handleHeartbeat(type: TEventType, data) {
    // 可以添加其他处理逻辑，例如更新最后收到心跳的时间等

    // 1. 检查心跳间隔，设置一个阈值（例如 10 秒）
    const heartbeatInterval = Date.now() - this.lastHeartbeatTime

    // 更新最后收到心跳的时间
    this.lastHeartbeatTime = Date.now()

    if (heartbeatInterval > this.maxHeartbeatInterval) {
      console.warn(
        `Server may not be responding properly. Heartbeat interval: ${heartbeatInterval}ms`
      )
      // 可以根据需要采取相应的措施，比如尝试重新连接
      this.reconnects(type, data, 2000)
    }

    // 2. 连接状态判断
    if (this.websocket?.readyState !== WebSocket.OPEN) {
      console.warn("WebSocket connection may be in an unstable state. Trying to reconnect...")
      // 可以根据需要进行重新连接
      this.reconnects(type, data, 2000)
    }

    // 3. 连接状态事件
    // 可以将connectionStatusChange写入TEventType，相应做添加，最后在这里派发处理事件
    // this.dispatchEvent("connectionStatusChange", {
    //   isConnected: this.websocket?.readyState === WebSocket.OPEN,
    //   lastHeartbeatTime: this.lastHeartbeatTime
    // })
  }
}
